﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.ComponentModel;
using System.Windows.Media;
using System.Collections.ObjectModel;


namespace VRacer
{
    /// <summary>
    /// Klasa definiuje podstawowe zachowania zawodnika. 
    /// Wywoluje zdarzenia przy podstawowych stanach zawodnika (Moving, Moved, Crashed, Actived..) za pomocą metod (OnMoving, OnMoved, OnCrashed, OnActived...).
    /// Zawiera podstawowe informacje o graczu (Title, Speed, Position, Laps, Moves) - odpowiednio nazwa gracza, aktualy wektor predkosc, pozycja, ilosc przebytych okrążeń i ilość dostępnych ruchów.
    /// Wykonuje akcje na zawodniku (Move, Crash, Hit..) - odpowiednio prosba o zmiane pozycji gracza (walidowane przez gre), zderzenie ze sciana lub z innym graczem w punkcie, gracz uderzony przez innego.
    /// 
    /// Zawiera statyczna kolekcje dostepnych kolorow graczy oraz metode do pobierania kolejnego dostepnego koloru (PlayerBase.Get(Playerbase player)) a takze do zwalniania zajetych kolorow (PlayerBase.Reset()).
    /// </summary>
    public abstract partial class PlayerBase : INotifyPropertyChanged
    {
        /// <summary>
        /// Wystepuje przy rozpoczeciu tury zawodnika
        /// </summary>
        public event EventHandler Activated;

        /// <summary>
        /// Wystepuje po zakonczeniu tury zawodnika
        /// </summary>
        public event EventHandler Deactivated;

        /// <summary>
        /// Wystepuje gdy zawodnik chce sie poruszyc
        /// </summary>
        public event EventHandler<PlayerMovingEventArgs> Moving;

        /// <summary>
        /// Wystepuje gdy zawodnik sie poruszyl
        /// </summary>
        public event EventHandler Moved;

        /// <summary>
        /// Wystepuje gdy zawodnik zostal uderzony przez innego zawodnika
        /// </summary>
        public event EventHandler<PlayerCollisionEventArgs> Collision;

        /// <summary>
        /// Wystepuje gdy zawodnik uderzyl w cos (sciana lub inny player)
        /// </summary>
        public event EventHandler<PlayerCrashedEventArgs> Crashed;

        private bool _isActive = false;
        private int _id;
        private bool _isFinished = false;
        private Point _position;
        private Point _resultantPosition;
        private Vector _speed = new Vector(0, 0);
        private int _moves = 0;
        private string _title;
        private int _laps = 0;
        private Color _playerColor;
        private Stats _stats = new Stats();

        


        public PlayerBase()
        {
            var TheSeed = (int)DateTime.Now.Ticks;
            var x = new Random(TheSeed * 1234);
            this.Color = GetColor(this);
        }

        public Brush BrushColor
        {
            get { return new SolidColorBrush(_playerColor); }
        }

        /// <summary>
        /// Kolor zawodnika
        /// </summary>
        /// <value>The color.</value>
        public Color Color
        {
            get { return _playerColor; }
            set
            {
                _playerColor = value;
            }
        }

        /// <summary>
        /// Gets or sets the ID.
        /// </summary>
        /// <value>The ID.</value>
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        /// <summary>
        /// Determinuje czy gracz juz zakonczyl rozgrywke
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is finished; otherwise, <c>false</c>.
        /// </value>
        public bool IsFinished
        {
            get { return _isFinished; }
            set { _isFinished = value; }
        }

        /// <summary>
        /// Nazwa zawodnika
        /// </summary>
        /// <value>The title.</value>
        public string Title
        {
            get { return _title; }
            set
            {
                _title = value;
            }
        }

        /// <summary>
        /// Aktualna predkosc zawodnika
        /// </summary>
        /// <value>The speed.</value>
        public Vector Speed
        {
            get { return _speed; }
            set
            {
                if (_speed != value)
                {
                    _speed = value;
                    OnPropertyChanged("Speed");
                    UpdateResultantPosition();
                }
            }
        }

        /// <summary>
        /// Gets the statistics.
        /// </summary>
        /// <value>The statistics.</value>
        public Stats Statistics
        {
            get
            {
                return _stats;
            }
        }

        /// <summary>
        /// Zwraca ilosc mozliwych do wykonania ruchow
        /// </summary>
        /// <value>The moves.</value>
        public int Moves
        {
            get { return _moves; }
            set { _moves = value; }
        }
        
        /// <summary>
        /// Aktualna pozycja zawodnika
        /// </summary>
        /// <value>The position.</value>
        public Point Position
        {
            get { return _position; }
            set
            {
                if (_position != value)
                {
                    _position = value;
                    OnPropertyChanged("Position");
                    UpdateResultantPosition();
                }
            }
        }

        /// <summary>
        /// Uaktualnia pozycje wypadkowa zawodnika
        /// </summary>
        private void UpdateResultantPosition()
        {
            ResultantPosition = Point.Add(Position, Speed);
        }

        /// <summary>
        /// Pozycja wypadkowa zawodnika (predkosc+pozycja)
        /// </summary>
        /// <value>The resultant position.</value>
        public Point ResultantPosition
        {
            get { return _resultantPosition; }
            protected set
            {
                _resultantPosition = value;
                OnPropertyChanged("ResultantPosition");
            }
        }

        /// <summary>
        /// Przesuwa zawodnika na podana pozycje
        /// </summary>
        /// <param name="point">The point.</param>
        public virtual void Move(Point point)
        {
            bool canMove = OnMoving(point);

            if (canMove)
            {
                MoveInternal(point);
            }
        }

        internal virtual void MoveInternal(Point point)
        {
            Speed = new Vector(point.X - Position.X, point.Y - Position.Y);
            Position = point;

            OnMoved();
        }

        /// <summary>
        /// Zaczyna ture zawodnika (aktywuje go)
        /// </summary>
        public virtual void StartTurn()
        {
            IsActive = true;
            OnActivated();
        }

        /// <summary>
        /// Konczy ture zawodnika (dezaktywuje go)
        /// </summary>
        public virtual void EndTurn()
        {
            IsActive = false;
            OnDeactivated();
        }

        protected virtual void OnActivated()
        {
            if (Activated != null)
                Activated(this, EventArgs.Empty);
        }

        protected virtual void OnCrashed(Point crashPoint)
        {
            if (Crashed != null)
                Crashed(this, new PlayerCrashedEventArgs(crashPoint));
        }

        protected virtual void OnDeactivated()
        {
            if (Deactivated != null)
                Deactivated(this, EventArgs.Empty);
        }

        protected virtual void OnMoved()
        {
            if (Moved != null)
                Moved(this, EventArgs.Empty);
        }

        protected virtual bool OnMoving(Point point)
        {
            var args = new PlayerMovingEventArgs(Position, point);

            if (Moving != null)
                Moving(this, args);

            return args.CanMove;
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string property)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(property));
        }

        #endregion

        protected virtual void OnCollision(PlayerBase player)
        {
            if (Collision != null)
                Collision(this, new PlayerCollisionEventArgs(player));
        }

        /// <summary>
        /// Robi graczowi kolizje na okreslonej pozycji i sprawia ze startuje w nastepnej kolejce z tej wlasnie pozycji
        /// </summary>
        /// <param name="crashPoint">The crash point.</param>
        public virtual void Crash(Point crashPoint)
        {
            Crash(crashPoint, crashPoint);
        }

        /// <summary>
        /// Robi graczowi kolizje na okreslonej pozycji i podaje skad ma zaczac kolejna kolejke.
        /// </summary>
        /// <param name="crashPoint">The crash point.</param>
        /// <param name="startPoint">The start point.</param>
        public virtual void Crash(Point crashPoint, Point startPoint)
        {
            Position = startPoint;
            OnCrashed(crashPoint);
        }

        /// <summary>
        /// Uderza w zawodnika innym zawodnikiem. Przez co zwieksza sie jego predkosc.
        /// </summary>
        /// <param name="player">The player.</param>
        public virtual void Hit(PlayerBase player)
        {
            Suspend(player.Speed);
            OnCollision(player);
        }

        /// <summary>
        /// Przyspiesza o podany wektor.
        /// </summary>
        /// <param name="vector">The vector.</param>
        public void Suspend(Vector vector)
        {
            this.Speed = Vector.Add(this.Speed, vector);
        }

        /// <summary>
        /// Momentalnie sie zatrzymuje.
        /// </summary>
        public void Stop()
        {
            this.Speed = new Vector(0, 0);
        }

        /// <summary>
        /// Zwraca wartosc okreslajaca czy zawodnik jest aktywny (czyli czy akurat on wykonuje ruch).
        /// </summary>
        /// <value><c>true</c> jesli aktywny; w przeciwnym wypadku, <c>false</c>.</value>
        public bool IsActive
        {
            get { return _isActive; }
            set
            {
                if (_isActive != value)
                {
                    _isActive = value;
                    OnPropertyChanged("IsActive");
                }
            }
        }

        /// <summary>
        /// Zwraca aktualne okrazenie
        /// </summary>
        /// <value>The laps.</value>
        public int Laps
        {
            get { return _laps; }
            set
            {
                if (_laps != value)
                {
                    _laps = value;
                    OnPropertyChanged("Laps");
                }
            }
        }

    }
}
